package com.hugang.juc.lock;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * 读写锁
 * * 保证一定能读到最新数据，修改期间，加写锁。写锁是一个排他锁（互斥锁），
 * * 读锁是一个共享锁（允许多个进程同时加读锁，但是单个进程不可同时加锁）
 * * 写锁没释放，读锁就必须等待
 * * 写 + 读：等待写锁释放
 * * 写 + 写：阻塞方式
 * * 读 + 写：写锁须等待读锁释放
 * * 读 + 读：相当于无锁，并发读，只会在redis中记录好所有当前的读锁，他没都会同时加锁成功
 * * 只要有写的存在，都必须等待。
 *
 * @author hg
 * @date 2020/8/20 16:41
 */
public class ReadWriteLockDemo {

    public static void main(String[] args) {
        MyCache myCache = new MyCache();


        //写数据，加写锁
        for (int i = 0; i < 5; i++) {
            int finalI = i;
            new Thread(() -> {
//                System.out.println(Thread.currentThread().getName() +" ---> 启动");
                myCache.put(finalI + "", finalI + "");

            }, String.valueOf(i)).start();

        }

        //读数据，加读锁
        for (int i = 6; i <= 10; i++) {
            int finalI = i;
            new Thread(() -> {
//                System.out.println(Thread.currentThread().getName() +" ---> 启动");
                myCache.get((finalI-6) + "");
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }, String.valueOf(i)).start();

        }
    }
}

class MyCache {
    ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
    Lock readLock = readWriteLock.readLock();
    Lock writeLock = readWriteLock.writeLock();
    private volatile Map<String, String> data = new HashMap();

    public void put(String key, String value) {
        writeLock.lock();
        try {
            System.out.println(Thread.currentThread().getName() + " ---> 开始写入数据：" + value);
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            data.put(key, value);
            System.out.println(Thread.currentThread().getName() + " ---> 完成写入");
        } finally {
            writeLock.unlock();
        }
    }

    public String get(String key) {
        readLock.lock();
        try {
            System.out.println(Thread.currentThread().getName() + " ---> 开始读取数据");
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            String value = data.get(key);
            System.out.println(Thread.currentThread().getName() + " ---> 完成读取数据:" + value);
            return value;
        } finally {
            readLock.unlock();
        }
    }
}
